/****************************************************************************\
 * @file     BR_Console.c
 * @author   傅帅
 * @date     2019/12/27
 * @brief    上位机控制台
 ******************************************************************************
 * @note    通讯协议：
              上传协议：
                已抛出错误：A+0x10+"err_name"(字符串均以0x00结尾)+模校验
                已创建动作：A+0x20+"action_name"+index+pars_num+模校验
                CPU占用率：A+0x30+cpu_rate(float)+模校验
            
              下发协议：
                清除错误：A+0x10+模校验
                动作触发：A+0x20+index+pars...(float)+模校验
            
            校验为前面所有数据(包括a)之和模256
 ****************************************************************************/
#include <string.h>
#include "FreeRTOS.h"
#include "queue.h"
#include "BR_Common.h"

/* 全局变量 */
TaskHandle_t BR_ConsoleHandlerTaskHandler;
TaskHandle_t BR_ConsolePosterTaskHandler;
/* 静态变量 */
static QueueHandle_t prvConsoleRecvQueue;               // 此队列只用于串口收到消息时向任务提供触发信号
static void prvOnConsoleRecv(uint8_t *pData, uint32_t Len);                         // 收到消息回调函数
static uint8_t prvTxBuffer[CONSOLE_TX_MAX_LEN], prvRxBuffer[CONSOLE_RX_MAX_LEN];    // 接收和发送缓冲区

/* -------------------------------------------------- Public ----------------------------------------------- */
/** @brief 控制台接收任务本体 **/
void BR_vConsoleHandlerTask(void)
{
    // 接收控制台指令并解析
    uint32_t RxLen;
    uint8_t Sum, *pMsg;          // 局部指针用于消息处理
    while(1)
    {
        xQueueReceive(prvConsoleRecvQueue, &RxLen, portMAX_DELAY);
        pMsg = prvRxBuffer;
        if(*pMsg != 'A') continue;  // 第一个字节若非attention，等下一帧
        Sum = 0;
        for(; pMsg < prvRxBuffer + RxLen - 1; pMsg++) Sum += *pMsg; // 到校验位前全加起来
        if((uint8_t)Sum == *pMsg)   // 若校验通过
        {
            pMsg = prvRxBuffer + 1;         // 重新回到A后一字节
            switch(*pMsg++)                 // 功能码判断
            {
                case 0x10:                  // 清除错误
                    BR_vClearErrorBits();
                    break;
                case 0x20:                  // 动作触发 index+pars...(float)  0号Action 2.33x3测试帧41 20 00 b8 1e 15 40 b8 1e 15 40 b8 1e 15 40 e2
                {
                    uint8_t Index, ParsNum;
                    Index = *pMsg++;
                    ParsNum = BR_ActionTbl[Index].ActionParNum;
                    memcpy(BR_ActionTbl[Index].pParsBuffer, pMsg, ParsNum * 4);             // 参数全为float,直接存到目标参数缓冲区中
                    BR_ActionTbl[Index].ActionFunc(BR_ActionTbl[Index].pParsBuffer);        // 触发目标动作
                    break;
                }
            }
        }
    }
}

/** @brief 控制台发送任务本体 **/
void BR_vConsolePosterTask(void)
{
    /* 发送已抛出错误 */
    BR_ErrorHandler_t pError = &BR_RaisedErrorList;
    while(pError->pNext != NULL)        // 遍历列表
    {
        // a+0x10+"err_name"+模校验
        pError = pError->pNext;                                     // 取不为空的Error
        uint8_t TxLen = BR_cMemCat(prvTxBuffer, sizeof( prvTxBuffer ), "A%c%s%c", 0x10, pError->ErrorName, 0x00);     // 拼接内存区，此函数会为缓冲区清0，故后续操作用字符串风格
        BR_vCheckByteCalculateAppend(prvTxBuffer, TxLen);           // 校验位计算并加入
        BR_iSend(BR_configCONSOLE_USART, prvTxBuffer, TxLen + 1);   // 发送
        vTaskDelay(5);                                              // 稍加延时防止不停发送
    }
    /* 发送已创建动作 */
    for(int i = 0; i < BR_ActionNum; i++)   // 遍历已创建动作
    {
        // a+0x20+"action_name"+index+pars_num+模校验
        uint8_t TxLen = BR_cMemCat(prvTxBuffer, sizeof( prvTxBuffer ), "A%c%s%c%c%c", 0x20, BR_ActionTbl[i].ActionName, 0x00, i, BR_ActionTbl[i].ActionParNum);  // 拼接
        BR_vCheckByteCalculateAppend(prvTxBuffer, TxLen);           // 校验位计算并加入
        BR_iSend(BR_configCONSOLE_USART, prvTxBuffer, TxLen + 1);   // 发送
        vTaskDelay(5);                                              // 稍加延时防止不停发送
    }
    /* 循环上发CPU占用率 */
    while(1)
    {
        // a+0x30+cpu_rate(float)+模校验
        uint8_t TxLen = BR_cMemCat(prvTxBuffer, sizeof( prvTxBuffer ), "A%c%f", 0x30, BR_CPU_Rate);  // 拼接
        BR_vCheckByteCalculateAppend(prvTxBuffer, TxLen);           // 校验位计算并加入
        BR_iSend(BR_configCONSOLE_USART, prvTxBuffer, TxLen + 1);   // 发送
        vTaskDelay(500);
    }
}

/** @brief 控制台初始化 **/
void BR_vConsoleInit(void)
{
    BR_vUsartInitWithDMA(   BR_configCONSOLE_USART, CONSOLE_BAUDRATE,
                            CONSOLE_TX_MAX_LEN, CONSOLE_TX_PRIO,
                            CONSOLE_RX_MAX_LEN, CONSOLE_RX_PRIO, prvOnConsoleRecv);
    prvConsoleRecvQueue = xQueueCreate(1,4);    // Len=1 Size=4Byte  传当前接收帧长度
}

/* -------------------------------------------------- Private ----------------------------------------------- */
/** @brief 控制台对应串口接收回调 **/
static void prvOnConsoleRecv(uint8_t *pData, uint32_t Len)
{
    BaseType_t IfWoken;
    memcpy(prvRxBuffer, pData, Len);    // 防止Usart中的缓冲区被更改，先复制到自己的缓冲区
    xQueueSendFromISR(prvConsoleRecvQueue, &Len, &IfWoken);    // 向任务发送通知
}
